<?php
declare(strict_types=1);

namespace app\shop\logic;

use app\shop\enums\order\OrderCrontabType;
use app\shop\enums\order\OrderEvaluate;
use app\shop\enums\order\OrderStatus;
use app\shop\exception\FailException;
use app\shop\exception\ParamsErrorException;
use app\shop\model\order\OrderDetailModel;
use app\shop\model\order\OrderEvaluateModel;
use app\shop\model\order\OrderModel;
use app\shop\model\user\UserModel;
use app\shop\service\LogService;
use app\shop\service\OrderService;
use think\db\BaseQuery;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\helper\Arr;
use think\Paginator;
use Throwable;

/**
 * @property OrderEvaluateModel $model
 */
class OrderEvaluateLogic extends BaseLogic
{
    public function __construct()
    {
        $this->model = new OrderEvaluateModel();
    }

    /**
     * 评价列表
     * @param array $params
     * @return array|Paginator
     */
    public function list(array $params): Paginator|array
    {
        $query = OrderEvaluateModel::getQuery();

        $query->where('status', 1);

        $this->buildEqualQuery($query, $params, [
            'goods_id' => 'goods_id'
        ]);

        if (Arr::get($params, 'type')) {
            $this->buildTabsWhere($query, $params['type']);
        }

        $query->withoutField(['update_time'])->order(['create_time' => 'desc']);

        return OrderEvaluateModel::paginateListQuery($query);
    }

    /**
     * 评价tab
     * @param array $params
     * @return array
     */
    public function tabs(array $params): array
    {
        $tabs = OrderEvaluate::getData();

        foreach ($tabs as &$item) {
            $tabsQuery     = $this->model->where('goods_id', $params['goods_id'])->where('status', 1);
            $item['count'] = $this->buildTabsWhere($tabsQuery, $item['value'])->count();
        }

        return $tabs;
    }

    private function buildTabsWhere($query, string $type): BaseQuery
    {
        return match ($type) {
            OrderEvaluate::ALL => $query,
            OrderEvaluate::GOOD => $query->whereIn('score', [4, 5]),
            OrderEvaluate::MEDIUM => $query->whereIn('score', [2, 3]),
            OrderEvaluate::BAD => $query->where('score', '=', 1),
        };
    }

    /**
     * 添加评价
     * @param array $params
     * @return array
     * @throws FailException
     * @throws ParamsErrorException|DbException
     */
    public function add(array $params): array
    {
        $user = UserModel::where(['id' => $params['user_id']])->findOrEmpty();
        if (!$user) {
            throw new FailException('评价用户信息异常,请稍后再试');
        }
        $params['user'] = $user;

        if ($params['order_id']) {
            // 评价所有订单详情
            return $this->evaluateAll($params);
        } else {
            // 评价单个订单详情
            return $this->evaluateSingle($params);
        }
    }

    /**
     * 评价单个
     * @param array $params
     * @return array
     * @throws FailException
     * @throws ParamsErrorException
     */
    public function evaluateSingle(array $params): array
    {
        $detail = OrderDetailModel::where([
            'id' => $params['detail_id'],
        ])->with(['orderRelation'])->findOrEmpty();
        if ($detail->isEmpty()) {
            throw new ParamsErrorException('订单详情不存在');
        }
        $evaluateCount = $this->model->where([
            'order_id'        => $detail->order_id,
            'order_detail_id' => $detail->id,
        ])->count();
        if ($evaluateCount > 0) {
            throw new ParamsErrorException('订单已评价');
        }

        $detailCount  = OrderDetailModel::where('order_id', $detail->order_id)->count();
        $evaluateData = $this->getEvaluateData([$detail->toArray()], $params);

        $orderService = new OrderService();
        $this->model->startTrans();
        try {
            $evaluate = $this->model->create(...$evaluateData);
            OrderDetailModel::where('id', $detail->id)->update(['evaluate_time' => time()]);
            $evaluateCount++;
            // 如果订单详情全部评价，则更新订单状态
            if ($detailCount == $evaluateCount) {
                LogService::orderStatusLog($detail->order_id, OrderStatus::COMPLETE, '用户完成订单评价', $params['user_id']);
                OrderModel::where('id', $detail->order_id)->update([
                    'status'        => OrderStatus::COMPLETE,
                    'evaluate_time' => time(),
                ]);

                (new OrderService())->afterEvaluate($detail->order_id);
            } else {
                $autoEvaluateTime = $orderService->getAutoEvaluateTime();
                $time             = $autoEvaluateTime + time();
                $orderService->addCrontab($detail->order_id, OrderCrontabType::ORDER_EVALUATE, $time, ['order_id' => $detail->order_id]);
            }

            $this->model->commit();
        } catch (Throwable $e) {
            $this->model->rollback();
            throw new FailException('评价失败,请稍后再试', null, $e);
        }

        return ['id' => $evaluate->id];
    }

    /**
     * 评价全部
     * @param array $params
     * @return array
     * @throws FailException
     */
    public function evaluateAll(array $params): array
    {
        // 评价所有订单详情
        try {
            $details = OrderDetailModel::where('order_id', $params['order_id'])->with(['orderRelation'])->select()->toArray();
        } catch (DataNotFoundException|ModelNotFoundException|DbException $e) {
            throw new FailException('订单详情不存在', null, $e);
        }

        $evaluateData = $this->getEvaluateData($details, $params);
        $this->model->startTrans();
        try {
            $this->model->saveAll($evaluateData);
            OrderDetailModel::where('order_id', $params['order_id'])->update(['evaluate_time' => time()]);
            $order = OrderModel::where('id', $params['order_id'])->findOrEmpty();
            LogService::orderStatusLog($params['order_id'], OrderStatus::COMPLETE, '用户完成订单评价', $params['user']->id);

            $order->status        = OrderStatus::COMPLETE;
            $order->evaluate_time = time();
            $order->save();

            (new OrderService())->afterEvaluate($order->id);

            $this->model->commit();
        } catch (Throwable $e) {
            $this->model->rollback();
            throw new FailException('评价失败', null, $e);
        }

        return ['order_id' => $params['order_id']];
    }

    /**
     * 获取默认评价内容
     * @param array $details
     * @param array $params
     * @return array
     */
    private function getEvaluateData(array $details, array $params): array
    {
        $evaluateData   = [];
        $defaultContent = '此用户没有填写评价内容';

        foreach ($details as $detail) {
            if ($detail['evaluate_time']) continue;
            $evaluateData[] = [
                'order_id'        => $detail['order_id'],
                'order_detail_id' => $detail['id'],
                'goods_id'        => $detail['goods_id'],
                'goods_name'      => $detail['goods_name'],
                'goods_spec'      => $detail['spec'],
                'user_id'         => $params['user']->id,
                'user_name'       => $params['user']->nickname,
                'user_avatar'     => $params['user']->avatar,
                'content'         => $params['content'] ?: $defaultContent,
                'images'          => $params['images'],
                'score'           => $params['score'],
            ];
        }

        return $evaluateData;
    }
}